﻿using StackExchange.Redis;
using System;
using System.Threading.Tasks;

namespace Qy.Core;

//知识点：Redis hash 是一个 string 类型的 field 和 value 的映射表，hash 特别适合用于存储对象。
public class RedisCacheManager : ICacheManager
{
    private readonly string redisConnenctionString;
    public volatile ConnectionMultiplexer redisConnection;
    private readonly object redisConnectionLock = new();
    public RedisCacheManager()
    {
        string redisConfiguration = AppSettings.App(new string[] { "RedisCaching", "ConnectionString" });//获取连接字符串

        if (string.IsNullOrWhiteSpace(redisConfiguration))
        {
            throw new ArgumentException("redis config is empty", nameof(redisConfiguration));
        }
        this.redisConnenctionString = redisConfiguration;
        this.redisConnection = GetRedisConnection();
    }

    /// <summary>
    /// 核心代码，获取连接实例
    /// 通过双if 加lock的方式，实现单例模式
    /// </summary>
    /// <returns></returns>
    private ConnectionMultiplexer GetRedisConnection()
    {
        //如果已经连接实例，直接返回
        if (this.redisConnection != null && this.redisConnection.IsConnected)
        {
            return this.redisConnection;
        }
        //加锁，防止异步编程中，出现单例无效的问题
        lock (redisConnectionLock)
        {
            this.redisConnection?.Dispose();
            try
            {
                this.redisConnection = ConnectionMultiplexer.Connect(redisConnenctionString);
            }
            catch (Exception)
            {
                throw new Exception("Redis服务未启用，请开启该服务");
            }
        }
        return this.redisConnection;
    }

    public bool Exists(string key)
    {
        return redisConnection.GetDatabase().KeyExists(key);
    }

    public void Remove(string key)
    {
        if (Exists(key) == true)
        {
            redisConnection.GetDatabase().KeyDelete(key); ;
        }
    }
    public async Task RemoveAsync(string key)
    {
        if (Exists(key) == true)
        {
            await redisConnection.GetDatabase().KeyDeleteAsync(key); ;
        }
    }

    public string GetValue(string key)
    {
        return redisConnection.GetDatabase().StringGet(key);
    }

    public async Task<string> GetValueAsync(string key)
    {
        return await redisConnection.GetDatabase().StringGetAsync(key);
    }

    public T Get<T>(string key)
    {
        var value = redisConnection.GetDatabase().StringGet(key);
        if (value.HasValue)
        {
            //需要用的反序列化，将Redis存储的Byte[]，进行反序列化
            return SerializeUtility.Deserialize<T>(value);
        }
        else
        {
            return default;
        }
    }

    public async Task<T> GetAsync<T>(string key)
    {
        var value = await redisConnection.GetDatabase().StringGetAsync(key);
        if (value.HasValue)
        {
            return SerializeUtility.Deserialize<T>(value);
        }
        else
        {
            return default;
        }
    }

    public void Set<T>(string key, T value, TimeSpan cacheTime)
    {
        if (value != null)
        {
            //序列化，将object值生成RedisValue
            redisConnection.GetDatabase().StringSet(key, SerializeUtility.Serialize(value), cacheTime);
        }
    }
    public async Task SetAsync<T>(string key, T value, TimeSpan cacheTime)
    {
        if (value != null)
        {
            //序列化，将object值生成RedisValue
            await redisConnection.GetDatabase().StringSetAsync(key, SerializeUtility.Serialize(value), cacheTime);
        }
    }
}
